package com.inspinia.upms.client.config;

import com.inspinia.config.common.config.RedisConfig;
import com.inspinia.config.common.config.ShiroPropConfig;
import com.inspinia.upms.client.filter.AccessControlFilter;
import com.inspinia.upms.client.filter.ClientAuthFilter;
import com.inspinia.upms.client.filter.CorsFilter;
import com.inspinia.upms.client.listener.TicketSessionListener;
import com.inspinia.upms.client.ream.ClientNonceRealm;
import com.inspinia.upms.client.ream.ClientShiroFilterFactoryBean;
import com.inspinia.upms.client.security.MyRedisSessionDao;
import com.inspinia.upms.common.filter.MyLogoutFilter;
import com.inspinia.upms.common.filter.UrlMatchFilter;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.*;

@Configuration
public class ShiroConfig {

    private static final String KICKOUT_URL = ShiroPropConfig.loginUrl + "&kickout=1";

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager clientSecurityManager() {
        DefaultWebSecurityManager webSecurityManager = new DefaultWebSecurityManager();
        webSecurityManager.setRealm(securityRealm());
        webSecurityManager.setSessionManager(defaultWebSessionManager());
        webSecurityManager.setCacheManager(redisCacheManager());
        return webSecurityManager;
    }


    /**
     *  开启shiro的注解
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }


    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(clientSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ClientShiroFilterFactoryBean shiroFilterFactoryBean = new ClientShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(clientSecurityManager());

        shiroFilterFactoryBean.setLoginUrl(ShiroPropConfig.loginUrl);
        shiroFilterFactoryBean.setSuccessUrl(ShiroPropConfig.successUrl);
        shiroFilterFactoryBean.setUnauthorizedUrl(ShiroPropConfig.unauthorizedUrl);

        Map filtersMap = new HashMap<>(5);
        filtersMap.put("urlm", urlMatchFilter());
        filtersMap.put("ssoFilter", customFilter());
        filtersMap.put("logout", myLogoutFilter());
        filtersMap.put("authc", accessControlFilter());
        filtersMap.put("cors", corsFilter());

        shiroFilterFactoryBean.setFilters(filtersMap);
        Map chainMap = new LinkedHashMap<>();

        chainMap.put("/", "anon");
        chainMap.put("/proxylogin", "anon");
        chainMap.put("/oklogin", "anon");
        chainMap.put("/logout", "logout");
        chainMap.put("/login", "anon");

        chainMap.put("/swagger-ui.html", "anon");
        chainMap.put("/swagger-resources", "anon");
        chainMap.put("/v2/api-docs", "anon");
        chainMap.put("/webjars/springfox-swagger-ui/**", "anon");
        chainMap.put("/unauthorized", "anon");
        chainMap.put("/public/**", "anon");
        chainMap.put("/pages/**", "user");
        chainMap.put("/home", "ssoFilter");
        chainMap.put("/upms/manage/**","user");
        chainMap.put("/**", "authc,urlm,ssoFilter,cors");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(chainMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 路径过滤器
     * @return
     */
    @Bean
    public UrlMatchFilter urlMatchFilter() {
        return new UrlMatchFilter();
    }

    /**
     * 自定义过滤器
     * @return
     */
    @Bean
    public ClientAuthFilter customFilter(){
        ClientAuthFilter clientAuthFilter = new ClientAuthFilter();
        clientAuthFilter.setKickoutUrl(KICKOUT_URL);
        clientAuthFilter.setRedisManager(redisManager());
        clientAuthFilter.setMaxSession(1);
        clientAuthFilter.setKickoutAfter(true);
        clientAuthFilter.setSessionManager(defaultWebSessionManager());
        return clientAuthFilter;
    }

    /**
     * 登出过滤器
     * @return
     */
    @Bean
    public MyLogoutFilter myLogoutFilter(){
        MyLogoutFilter myLogoutFilter = new MyLogoutFilter();
        myLogoutFilter.setRedirectUrl(ShiroPropConfig.loginUrl);
        myLogoutFilter.setRedisManager(redisManager());
        return myLogoutFilter;
    }

    /**
     * token过滤器
     * @return
     */
    @Bean
    public AccessControlFilter accessControlFilter(){
        return new AccessControlFilter();
    }

    /**
     * 跨域过滤器
     * @return
     */
    @Bean
    public CorsFilter corsFilter(){
        return new CorsFilter();
    }
    /**
     * 注册securityManager
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactoryBean.setArguments(new Object[]{clientSecurityManager()});
        return methodInvokingFactoryBean;
    }

    @Bean(name = "myRealm")
    public ClientNonceRealm securityRealm() {
        ClientNonceRealm securityRealm = new ClientNonceRealm();
        securityRealm.setCachingEnabled(false);
        return securityRealm;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /** 客户端统一会话管理 开始*/
    @Bean
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager cacheManager = new RedisCacheManager();
        cacheManager.setRedisManager(redisManager());
        return cacheManager;
    }

    @Bean(name = "sessionManager")
    public DefaultWebSessionManager defaultWebSessionManager() {
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        //客户端30分钟后过期，服务端2100秒
        defaultWebSessionManager.setGlobalSessionTimeout(1800000);
        defaultWebSessionManager.setSessionDAO(redisSessionDAO());
        defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
        defaultWebSessionManager.setSessionListeners(sessionListeners());
        defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);
        return defaultWebSessionManager;
    }

    @Bean(name = "sessionListener")
    public Collection<SessionListener> sessionListeners() {
        TicketSessionListener ticketSessionListener =new TicketSessionListener();
        Collection<SessionListener> listeners = new ArrayList<>();
        listeners.add(ticketSessionListener);
        return listeners;
    }
    /**
     * redis管理器
     * @return
     */
    @Bean(name = "redisManager")
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(RedisConfig.host);
        redisManager.setPort(RedisConfig.port);
        redisManager.setTimeout(RedisConfig.timeout);
        redisManager.setPassword(RedisConfig.password);
        redisManager.setExpire(RedisConfig.expire);
        return redisManager;
    }

    /**
     * redis缓存的dao
     * @return
     */
    @Bean(name = "redisSessionDAO")
    public MyRedisSessionDao redisSessionDAO() {
        MyRedisSessionDao sessionDao = new MyRedisSessionDao();
        sessionDao.setRedisManager(redisManager());
        return sessionDao;
    }
    /** 客户端统一会话管理结束 */
}